package Dungeon;

import be.kuleuven.cs.som.annotate.Basic;
import be.kuleuven.cs.som.annotate.Raw;
import Border.BorderType;
import Border.Direction;
import Coordinates.Axis;
import Square.*;
/**
 * 
 * @author Koen Certyn en Michiel Meersmans
 *
 */
public class Shaft<K extends Square> extends RegularDungeon<K> {
	
	/**
	 * Creates a new Shaft
	 * 
	 * @param 		length
	 * 				The maximum length of the shaft dungeon
	 * 
	 * @param		shaftDirection
	 * 				The direction in which the shaft is situated 	
	 * 
	 * @effect		This shaft is initialized as a new RegularDungeon with 1,1,1-Dimensions
	 * 				| super(1, 1, 1)
	 * 
	 * @post		Sets the shaftDirection
	 * 				| new.getShaftDirection() == shaftDirection
	 * 
	 * @post		Sets the length to the corresponding shaftDirection
	 * 				| new.getDungeonDimension(shaftDirection) == length			
	 */

	public Shaft(long length, 
			Axis shaftDirection)  {
		super(1, 1, 1);
		setShaftDirection(shaftDirection);
		this.setDungeonDimension(length, shaftDirection);
	}
	
	
	/**
	 * Adds the square to the Shaft type dungeon
	 * 
	 * @param		square
	 * 				Square that is going to be added
	 * 
	 * @param		x
	 * 				x Dimension where the square is going to be added
	 * 
	 * @param		y
	 * 				y Dimension where the square is going to be added
	 * 
	 * @param		z
	 * 				z Dimension where the square is going to be added
	 * 
	 * @effect		The square is added to this RegularDungeon
	 * 				| super.addSquare(Square, x ,y, z)
	 * 
	 * @throws		If the square is not a valid square that needs to be added, a exception is thrown.
	 * 				| isValidCoordinateChecker(x, y, z) && isValidShaftSquare(square)
	 */
	@Override 
	public void addSquare(K square, long x, long y, long z) throws IllegalArgumentException {
		if ( isValidCoordinateChecker(x, y, z) && isValidShaftSquare(square)) 
			super.addSquare(square, x, y, z);
		else
			throw new IllegalArgumentException();
	}
	
	
	/**
	 * Direction in which the Shaft is located.
	 */
	private Axis shaftDirection;
	
	/**
	 * Adds the given shaftDirection to the parameter
	 * 
	 * @param 		shaftDirection
	 * 				Direction in which the Shaft is located.
	 * 
	 * @post		The parameter shaftDirection is set to the correct value
	 * 				| new.getShaftDirection() == shaftDirection
	 * 
	 * @throws		IllegalArgumentException
	 * 				The given shaftDirection is not effective
	 * 				|  (shaftDirection == null)
	 */
	@Raw
	public void setShaftDirection(Axis shaftDirection)throws IllegalArgumentException{
		if (shaftDirection == null)
			throw new IllegalArgumentException();
		this.shaftDirection = shaftDirection;
	}
	
	/**
	 * Get method that returns the shaftDirection
	 * 
	 */
	@Basic
	public Axis getShaftDirection(){
		return this.shaftDirection;
	}
	
	/**
	 * Method that checks if the coordinates on which the square is going to be added, are valid.
	 * 
	 * @param 		x
	 * 				Given x coordinate which is going to be checked
	 * 
	 * @param 		y
	 * 				Given y coordinate which is going to be checked
	 * 
	 * @param 		z
	 * 				Given z coordinate which is going to be checked
	 * 
	 * @return		Returns true if the Coordinates are valid.
	 * 				| if (getShaftDirection() == Dungeon.Axis.X_AXIS)
	 *				|	result == (x >= 0  && y == 0 && z == 0)
	 * 				| if (getShaftDirection() == Dungeon.Axis.Y_AXIS)
	 *				|	result == (x == 0 && z == 0 && y >= 0)
	 * 				| result == (x == 0 && y == 0  && z >= 0)
	 */
	private boolean isValidCoordinateChecker(long x, long y, long z){
		if (getShaftDirection() == Axis.X_AXIS)
			return ( y == 0 && z == 0 && x >= 0);
		else if (getShaftDirection() == Axis.Y_AXIS)
			return (x == 0 && z == 0 && y >= 0);
		return (x == 0 && y == 0  && z >= 0);
	}
	
	/**
	 * Method that checks if the square does not have any forbidden borders in the direction of the Shaft.
	 * 
	 * @param 		square
	 * 				Square who's borders are going to be checked.
	 * 	
	 * @return		Returns true if the borders, in the direction of the shaft, are empty.  This is checked in the
	 * 				x_axis.
	 * 				| if (getShaftDirection() == Dungeon.Axis.X_AXIS){
	 *				|  	result == ( (square.getBorderAt(Border.Direction.EAST).getBorderType() == Border.BorderType.EMPTY) && 
	 *				|			(square.getBorderAt(Border.Direction.EAST).getBorderType() == Border.BorderType.WALL) &&
	 *				|			(square.getBorderAt(Border.Direction.WEST).getBorderType() == Border.BorderType.WALL) && 
	 *				|			(square.getBorderAt(Border.Direction.WEST).getBorderType() == Border.BorderType.EMPTY))
	 *	
	 * @return		Returns true if the borders, in the direction of the shaft, are empty.  This is checked in the
	 * 				y_axis.
	 * 				| if (getShaftDirection() == Dungeon.Axis.Y_AXIS){
	 *				|	result == ( (square.getBorderAt(Border.Direction.NORTH).getBorderType() == Border.BorderType.WALL)&& 
	 *				|			(square.getBorderAt(Border.Direction.NORTH).getBorderType() == Border.BorderType.EMPTY) &&
	 *				|			(square.getBorderAt(Border.Direction.SOUTH).getBorderType() == Border.BorderType.EMPTY) && 
	 *				|			(square.getBorderAt(Border.Direction.SOUTH).getBorderType() == Border.BorderType.WALL))
	 *
	 * @return		Returns true if the borders, in the direction of the shaft, are empty.  This is checked in the
	 * 				z_axis.
	 *				| result == ( (square.getBorderAt(Border.Direction.CEILING).getBorderType() == Border.BorderType.WALL)&& 
	 *				|			(square.getBorderAt(Border.Direction.CEILING).getBorderType() == Border.BorderType.EMPTY) &&
	 *				|			(square.getBorderAt(Border.Direction.FLOOR).getBorderType() == Border.BorderType.WALL) && 
	 *				|			(square.getBorderAt(Border.Direction.FLOOR).getBorderType() == Border.BorderType.EMPTY))
	 *	
	 */
	private boolean isValidShaftSquare(Square square){
		if (getShaftDirection() == Axis.X_AXIS){
			return ( (square.getBorderAt(Direction.EAST).getBorderType() == BorderType.EMPTY) || 
					(square.getBorderAt(Direction.EAST).getBorderType() == BorderType.WALL) &&
					(square.getBorderAt(Direction.WEST).getBorderType() == BorderType.WALL) ||
					(square.getBorderAt(Direction.WEST).getBorderType() == BorderType.EMPTY));
		}
		else if (getShaftDirection() == Axis.Y_AXIS){
			return ( (square.getBorderAt(Direction.NORTH).getBorderType() == BorderType.EMPTY)|| 
					(square.getBorderAt(Direction.NORTH).getBorderType() == BorderType.WALL) && 
					(square.getBorderAt(Direction.SOUTH).getBorderType() == BorderType.EMPTY) || 
					(square.getBorderAt(Direction.SOUTH).getBorderType() == BorderType.WALL)) ;
		}
		return ( (square.getBorderAt(Direction.CEILING).getBorderType() == BorderType.EMPTY) || 
				(square.getBorderAt(Direction.CEILING).getBorderType() == BorderType.WALL)&&
				(square.getBorderAt(Direction.FLOOR).getBorderType() == BorderType.EMPTY) ||
				(square.getBorderAt(Direction.FLOOR).getBorderType() == BorderType.WALL));
	}
	
	/**
	 * Method that sets the maxDimensions to the given maxDimension
	 * 
	 * @param 		maxDimension
	 * 				Parameter that contains the value of the maxDimension
	 * 
	 * @param 		axis
	 * 				Enum that resembles the axis on which the maxDimension has effect on
	 * 
	 * @effect		Sets the dimension of the RegularDungeon
	 *				| new.getDungeonDimensions(axis) == maxDimension;
	 *
	 * @throws 		IllegalArgumentException
	 * 				Throws a exception if the the axis being changed isn't the shaft direction
	 * 				| (axis != getShaftDirection())
	 */
	@Raw
	@Override
	public void setDungeonDimension(long maxDimension, Axis axis) throws IllegalArgumentException {
		if(axis != getShaftDirection() && maxDimension != 1 )
			throw new IllegalArgumentException();
		super.setDungeonDimension(maxDimension, axis);
	}
	
}
	
